//-----------------------------------------------------------------------------
// Copyright (c) 2013 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#include "console/console.h"
#include "compiler.h"
#include "console/consoleInternal.h"

using namespace Compiler;

/// @file
///
/// TorqueScript AST node allocators.
///
/// These static methods exist to allocate new AST node for the compiler. They
/// all allocate memory from the consoleAllocator for efficiency, and often take
/// arguments relating to the state of the nodes. They are called from gram.y
/// (really gram.c) as the lexer analyzes the script code.

//------------------------------------------------------------

BreakStmtNode* BreakStmtNode::alloc(S32 lineNumber)
{
   BreakStmtNode* ret = (BreakStmtNode*)consoleAlloc(sizeof(BreakStmtNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   return ret;
}

ContinueStmtNode* ContinueStmtNode::alloc(S32 lineNumber)
{
   ContinueStmtNode* ret = (ContinueStmtNode*)consoleAlloc(sizeof(ContinueStmtNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   return ret;
}

ReturnStmtNode* ReturnStmtNode::alloc(S32 lineNumber, ExprNode* expr)
{
   ReturnStmtNode* ret = (ReturnStmtNode*)consoleAlloc(sizeof(ReturnStmtNode));
   constructInPlace(ret);
   ret->expr = expr;
   ret->dbgLineNumber = lineNumber;

   return ret;
}

IfStmtNode* IfStmtNode::alloc(S32 lineNumber, ExprNode* testExpr, StmtNode* ifBlock, StmtNode* elseBlock, bool propagate)
{
   IfStmtNode* ret = (IfStmtNode*)consoleAlloc(sizeof(IfStmtNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;

   ret->testExpr = testExpr;
   ret->ifBlock = ifBlock;
   ret->elseBlock = elseBlock;
   ret->propagate = propagate;

   return ret;
}

LoopStmtNode* LoopStmtNode::alloc(S32 lineNumber, ExprNode* initExpr, ExprNode* testExpr, ExprNode* endLoopExpr, StmtNode* loopBlock, bool isDoLoop)
{
   LoopStmtNode* ret = (LoopStmtNode*)consoleAlloc(sizeof(LoopStmtNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->testExpr = testExpr;
   ret->initExpr = initExpr;
   ret->endLoopExpr = endLoopExpr;
   ret->loopBlock = loopBlock;
   ret->isDoLoop = isDoLoop;

   // Deal with setting some dummy constant nodes if we weren't provided with
   // info... This allows us to play nice with missing parts of for(;;) for
   // instance.
   if (!ret->testExpr) ret->testExpr = IntNode::alloc(lineNumber, 1);

   return ret;
}

IterStmtNode* IterStmtNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode* containerExpr, StmtNode* body, bool isStringIter)
{
   IterStmtNode* ret = (IterStmtNode*)consoleAlloc(sizeof(IterStmtNode));
   constructInPlace(ret);

   ret->dbgLineNumber = lineNumber;
   ret->varName = varName;
   ret->containerExpr = containerExpr;
   ret->body = body;
   ret->isStringIter = isStringIter;

   return ret;
}

FloatBinaryExprNode* FloatBinaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* left, ExprNode* right)
{
   FloatBinaryExprNode* ret = (FloatBinaryExprNode*)consoleAlloc(sizeof(FloatBinaryExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;

   ret->op = op;
   ret->left = left;
   ret->right = right;

   return ret;
}

IntBinaryExprNode* IntBinaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* left, ExprNode* right)
{
   IntBinaryExprNode* ret = (IntBinaryExprNode*)consoleAlloc(sizeof(IntBinaryExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;

   ret->op = op;
   ret->left = left;
   ret->right = right;

   return ret;
}

StreqExprNode* StreqExprNode::alloc(S32 lineNumber, ExprNode* left, ExprNode* right, bool eq)
{
   StreqExprNode* ret = (StreqExprNode*)consoleAlloc(sizeof(StreqExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->left = left;
   ret->right = right;
   ret->eq = eq;

   return ret;
}

StrcatExprNode* StrcatExprNode::alloc(S32 lineNumber, ExprNode* left, ExprNode* right, int appendChar)
{
   StrcatExprNode* ret = (StrcatExprNode*)consoleAlloc(sizeof(StrcatExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->left = left;
   ret->right = right;
   ret->appendChar = appendChar;

   return ret;
}

CommaCatExprNode* CommaCatExprNode::alloc(S32 lineNumber, ExprNode* left, ExprNode* right)
{
   CommaCatExprNode* ret = (CommaCatExprNode*)consoleAlloc(sizeof(CommaCatExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->left = left;
   ret->right = right;

   return ret;
}

IntUnaryExprNode* IntUnaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* expr)
{
   IntUnaryExprNode* ret = (IntUnaryExprNode*)consoleAlloc(sizeof(IntUnaryExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->op = op;
   ret->expr = expr;
   return ret;
}

FloatUnaryExprNode* FloatUnaryExprNode::alloc(S32 lineNumber, S32 op, ExprNode* expr)
{
   FloatUnaryExprNode* ret = (FloatUnaryExprNode*)consoleAlloc(sizeof(FloatUnaryExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->op = op;
   ret->expr = expr;
   return ret;
}

VarNode* VarNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex)
{
   VarNode* ret = (VarNode*)consoleAlloc(sizeof(VarNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->varName = varName;
   ret->arrayIndex = arrayIndex;
   ret->defaultValue = NULL;
   return ret;
}

VarNode* VarNode::allocParam(S32 lineNumber, StringTableEntry varName, ExprNode* defaultValue)
{
   VarNode* ret = (VarNode*)consoleAlloc(sizeof(VarNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->varName = varName;
   ret->arrayIndex = NULL;
   ret->defaultValue = defaultValue;
   return ret;
}

IntNode* IntNode::alloc(S32 lineNumber, S32 value)
{
   IntNode* ret = (IntNode*)consoleAlloc(sizeof(IntNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->value = value;
   return ret;
}

ConditionalExprNode* ConditionalExprNode::alloc(S32 lineNumber, ExprNode* testExpr, ExprNode* trueExpr, ExprNode* falseExpr)
{
   ConditionalExprNode* ret = (ConditionalExprNode*)consoleAlloc(sizeof(ConditionalExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->testExpr = testExpr;
   ret->trueExpr = trueExpr;
   ret->falseExpr = falseExpr;
   ret->integer = false;
   return ret;
}

FloatNode* FloatNode::alloc(S32 lineNumber, F64 value)
{
   FloatNode* ret = (FloatNode*)consoleAlloc(sizeof(FloatNode));
   constructInPlace(ret);

   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->value = value;
   return ret;
}

StrConstNode* StrConstNode::alloc(S32 lineNumber, const char* str, bool tag, bool doc)
{
   StrConstNode* ret = (StrConstNode*)consoleAlloc(sizeof(StrConstNode));
   constructInPlace(ret);
   S32 len = dStrlen(str);

   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->str = (char*)consoleAlloc(len + 1);
   ret->tag = tag;
   ret->doc = doc;
   dStrcpy(ret->str, str, len + 1);
   ret->str[len] = '\0';

   return ret;
}

ConstantNode* ConstantNode::alloc(S32 lineNumber, StringTableEntry value)
{
   ConstantNode* ret = (ConstantNode*)consoleAlloc(sizeof(ConstantNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->value = value;
   return ret;
}

AssignExprNode* AssignExprNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex, ExprNode* expr)
{
   AssignExprNode* ret = (AssignExprNode*)consoleAlloc(sizeof(AssignExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->varName = varName;
   ret->expr = expr;
   ret->arrayIndex = arrayIndex;

   return ret;
}

AssignOpExprNode* AssignOpExprNode::alloc(S32 lineNumber, StringTableEntry varName, ExprNode* arrayIndex, ExprNode* expr, S32 op)
{
   AssignOpExprNode* ret = (AssignOpExprNode*)consoleAlloc(sizeof(AssignOpExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->varName = varName;
   ret->expr = expr;
   ret->arrayIndex = arrayIndex;
   ret->op = op;
   return ret;
}

TTagSetStmtNode* TTagSetStmtNode::alloc(S32 lineNumber, StringTableEntry tag, ExprNode* valueExpr, ExprNode* stringExpr)
{
   TTagSetStmtNode* ret = (TTagSetStmtNode*)consoleAlloc(sizeof(TTagSetStmtNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->tag = tag;
   ret->valueExpr = valueExpr;
   ret->stringExpr = stringExpr;
   return ret;
}

TTagDerefNode* TTagDerefNode::alloc(S32 lineNumber, ExprNode* expr)
{
   TTagDerefNode* ret = (TTagDerefNode*)consoleAlloc(sizeof(TTagDerefNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->expr = expr;
   return ret;
}

TTagExprNode* TTagExprNode::alloc(S32 lineNumber, StringTableEntry tag)
{
   TTagExprNode* ret = (TTagExprNode*)consoleAlloc(sizeof(TTagExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->tag = tag;
   return ret;
}

FuncCallExprNode* FuncCallExprNode::alloc(S32 lineNumber, StringTableEntry funcName, StringTableEntry nameSpace, ExprNode* args, bool dot)
{
   FuncCallExprNode* ret = (FuncCallExprNode*)consoleAlloc(sizeof(FuncCallExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->funcName = funcName;
   ret->nameSpace = nameSpace;
   ret->args = args;
   if (dot)
      ret->callType = MethodCall;
   else if (nameSpace != NULL)
   {
      if (dStricmp(nameSpace, "Parent") == 0)
         ret->callType = ParentCall;
      else
         ret->callType = StaticCall;
   }
   else
      ret->callType = FunctionCall;
   return ret;
}

AssertCallExprNode* AssertCallExprNode::alloc(S32 lineNumber, ExprNode* testExpr, const char* message)
{
#ifdef TORQUE_ENABLE_SCRIPTASSERTS

   AssertCallExprNode* ret = (AssertCallExprNode*)consoleAlloc(sizeof(FuncCallExprNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->testExpr = testExpr;
   ret->message = message ? message : "TorqueScript assert!";
   return ret;

#else

   return NULL;

#endif
}

SlotAccessNode* SlotAccessNode::alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* arrayExpr, StringTableEntry slotName)
{
   SlotAccessNode* ret = (SlotAccessNode*)consoleAlloc(sizeof(SlotAccessNode));
   constructInPlace(ret);
   ret->optimizedNode = NULL;
   ret->dbgLineNumber = lineNumber;
   ret->objectExpr = objectExpr;
   ret->arrayExpr = arrayExpr;
   ret->slotName = slotName;
   return ret;
}

InternalSlotAccessNode* InternalSlotAccessNode::alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* slotExpr, bool recurse)
{
   InternalSlotAccessNode* ret = (InternalSlotAccessNode*)consoleAlloc(sizeof(InternalSlotAccessNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->objectExpr = objectExpr;
   ret->slotExpr = slotExpr;
   ret->recurse = recurse;
   return ret;
}

SlotAssignNode* SlotAssignNode::alloc(S32 lineNumber, ExprNode* objectExpr, ExprNode* arrayExpr, StringTableEntry slotName, ExprNode* valueExpr, U32 typeID /* = -1 */)
{
   SlotAssignNode* ret = (SlotAssignNode*)consoleAlloc(sizeof(SlotAssignNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->objectExpr = objectExpr;
   ret->arrayExpr = arrayExpr;
   ret->slotName = slotName;
   ret->valueExpr = valueExpr;
   ret->typeID = typeID;
   return ret;
}

SlotAssignOpNode* SlotAssignOpNode::alloc(S32 lineNumber, ExprNode* objectExpr, StringTableEntry slotName, ExprNode* arrayExpr, S32 op, ExprNode* valueExpr)
{
   SlotAssignOpNode* ret = (SlotAssignOpNode*)consoleAlloc(sizeof(SlotAssignOpNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->objectExpr = objectExpr;
   ret->arrayExpr = arrayExpr;
   ret->slotName = slotName;
   ret->op = op;
   ret->valueExpr = valueExpr;
   return ret;
}

ObjectDeclNode* ObjectDeclNode::alloc(S32 lineNumber, ExprNode* classNameExpr, ExprNode* objectNameExpr, ExprNode* argList, StringTableEntry parentObject, SlotAssignNode* slotDecls, ObjectDeclNode* subObjects, bool isDatablock, bool classNameInternal, bool isSingleton)
{
   ObjectDeclNode* ret = (ObjectDeclNode*)consoleAlloc(sizeof(ObjectDeclNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->optimizedNode = NULL;
   ret->classNameExpr = classNameExpr;
   ret->objectNameExpr = objectNameExpr;
   ret->argList = argList;
   ret->slotDecls = slotDecls;
   ret->subObjects = subObjects;
   ret->isDatablock = isDatablock;
   ret->isClassNameInternal = classNameInternal;
   ret->isSingleton = isSingleton;
   ret->failOffset = 0;
   if (parentObject)
      ret->parentObject = parentObject;
   else
      ret->parentObject = StringTable->insert("");
   return ret;
}

FunctionDeclStmtNode* FunctionDeclStmtNode::alloc(S32 lineNumber, StringTableEntry fnName, StringTableEntry nameSpace, VarNode* args, StmtNode* stmts)
{
   FunctionDeclStmtNode* ret = (FunctionDeclStmtNode*)consoleAlloc(sizeof(FunctionDeclStmtNode));
   constructInPlace(ret);
   ret->dbgLineNumber = lineNumber;
   ret->fnName = fnName;
   ret->args = args;
   ret->stmts = stmts;
   ret->nameSpace = nameSpace;
   ret->package = NULL;
   return ret;
}
